﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Core;

namespace ClickrDesktop.Managers
{
    class SystemWatcher
    {
        public FileSystemWatcher watcher = null;

        // Detect Change in file at particular location
        // Set Path in FormClickrDesktop => PathFileWatcher (App Config)
        #region File Watcher and Events
        public void SystemFileWatcher()
        {
            watcher = new FileSystemWatcher();

            if (FormClickrDesktop.PathFileWatcher.ToString() != String.Empty)
            {
                watcher.Path = FormClickrDesktop.PathFileWatcher;

                watcher.IncludeSubdirectories = true;
                watcher.NotifyFilter = NotifyFilters.FileName;

                watcher.Changed += new FileSystemEventHandler(OnChanged);
                watcher.Created += new FileSystemEventHandler(OnChanged);
                watcher.Deleted += new FileSystemEventHandler(OnChanged);
                watcher.Renamed += new RenamedEventHandler(OnRenamed);

                watcher.EnableRaisingEvents = true;
            }
        }

        public void RemoveHadlers()
        {
            watcher.Changed -= new FileSystemEventHandler(OnChanged);
            watcher.Created -= new FileSystemEventHandler(OnChanged);
            watcher.Deleted -= new FileSystemEventHandler(OnChanged);
            watcher.Renamed -= new RenamedEventHandler(OnRenamed);
        }

        private void OnChanged(object source, FileSystemEventArgs e)
        {
            Managers.LogManager.EnterInLogFile(String.Format("{0}\n{1}", e.FullPath, e.ChangeType.ToString().ToUpper()));
        }

        // Detect the change in file content and pushes it to Clickr
        private void OnRenamed(object source, RenamedEventArgs e)
        {
            Managers.LogManager.EnterInLogFile(String.Format("{0}\nRENAMED TO\n{1}", e.OldFullPath, e.FullPath));

            FileInfo file = new FileInfo(e.FullPath);
            if (file.Extension.Equals(".tpqx"))
            {
                bool auto_sync = Boolean.Parse(FormClickrDesktop.DtSesstings.Rows[FormClickrDesktop.DtSessionRowNumber]["sync"].ToString());
                if (auto_sync)
                    CreateThreadForPush(e.FullPath);
                else
                    if (!FormClickrDesktop.stackChangedFiles.Contains(e.FullPath))
                        FormClickrDesktop.stackChangedFiles.Push(e.FullPath);
            }
        }

        // Create thread against changed file to push (one-to-one-coresspondence)
        public void CreateThreadForPush(string path)
        {
            // Create one thread against one change
            ThreadStart Upload_XML_File = delegate { Worker(path); };
            Thread thread = new Thread(Upload_XML_File);

            thread.IsBackground = true;
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }
        #endregion

        public void Worker(String path, bool retry = false)
        {
            String new_file = path;
            String path_zip_tmp = String.Format("{0}\\tmp{1}.zip", FormClickrDesktop.PathTmpFolder, DateTime.Now.ToString("yyyyMMddHHmmss"));

            try
            {
                File.Copy(new_file, path_zip_tmp);
                bool response = ExtractZipFile(path_zip_tmp, FormClickrDesktop.PathTmpFolder);
                if (response)
                {
                    String xmlFile = path_zip_tmp.Replace("zip", "xml");
                    String result = new Managers.User().pushXML(FormClickrDesktop.user, xmlFile);

                    if (result.ToLower().Contains("success"))
                        Managers.LogManager.EnterInLogFile(String.Format("File {0} \nPosted Successfully.", new_file));
                    else
                    {
                        Managers.LogManager.EnterInLogFile(String.Format("Failed to upload file {0} \nException \n{1}", new_file, result));
                        // If it returns unauthorized exception (Timeout) then renew the user object
                        if (result.ToLower().Contains("(401) unauthorized"))
                        {
                            FormClickrDesktop.user = new Managers.User().login(FormClickrDesktop.session.username, FormClickrDesktop.session.password);
                            FormClickrDesktop.session.session_id = FormClickrDesktop.user.session_id;

                            if (!retry)
                                Worker(path, true);
                            return;
                        }

                    }

                    // Clearing up the Tmp Folder mess
                    if (File.Exists(xmlFile))
                        File.Delete(xmlFile);
                    if (File.Exists(path_zip_tmp))
                        File.Delete(path_zip_tmp);
                }
                else
                    Managers.LogManager.EnterInLogFile(String.Format("Fail to extract file {0}", path_zip_tmp));
            }
            catch (Exception ex) { Managers.LogManager.EnterInLogFile(String.Format("Exception\n{0}\n{1}", ex.Message, ex.StackTrace)); }
        }

        // Get Zip file and Location for its extraction
        private bool ExtractZipFile(String ArchiveFile, String Destination)
        {
            ZipFile zip_file = null;
            bool response = false;

            try
            {
                FileStream file_stream = File.OpenRead(ArchiveFile);
                zip_file = new ZipFile(file_stream);

                foreach (ZipEntry zipEntry in zip_file)
                {
                    if (!zipEntry.IsFile)
                        continue;

                    String entryFileName = new FileInfo(ArchiveFile).Name.Replace("zip", "xml");

                    byte[] buffer = new byte[4096];     // 4K is optimum
                    Stream zipStream = zip_file.GetInputStream(zipEntry);

                    // Manipulate the output filename here as desired.
                    String fullZipToPath = Path.Combine(Destination, entryFileName);
                    string directoryName = Path.GetDirectoryName(fullZipToPath);
                    if (directoryName.Length > 0)
                        Directory.CreateDirectory(directoryName);

                    using (FileStream streamWriter = File.Create(fullZipToPath))
                        StreamUtils.Copy(zipStream, streamWriter, buffer);
                }
                response = true;
            }
            catch (Exception ex)
            {
                response = false;
            }
            finally
            {
                if (zip_file != null)
                {
                    zip_file.IsStreamOwner = true;
                    zip_file.Close();
                }
            }
            return response;
        }
    }
}
